#include "parser.h"

TipoDato * Parser::crear_tipo_desde_cadena(std::string& tipo)  throw(ExcepSintaxisParser){
	
	if(tipo.compare("int") == 0 )
		return (new TipoInt());
	if(tipo.compare("string") == 0 )
		return (new TipoString());
	return NULL;
}	

std::vector<std::string> Parser::obtener_campos(std::string cadena){
	//Vble que indica si estoy o no spliteando
	bool prendido = true;
	//creo el vector de strings para devolver
	std::vector<std::string> campos;
	//string que va conteniendo el valor de cada campo
	std::string campo_actual;
	unsigned int cont = 0;
	//si la cadena no esta vacia
	if(cadena != ""){
		//recorro toda la cadena
		while (cont < cadena.size()) {
			if ((cadena[cont] == ',') && (prendido)) {
				campos.push_back(campo_actual);
				campo_actual.clear();	
			} else {
				campo_actual += cadena[cont];
				if (cadena[cont] == '|')
					prendido = !prendido;	
			}
			cont++;	
		}
		//agrego el ultimo param
		campos.push_back(campo_actual);
	}
	//devuelvo el vector
	return campos;
}
		
std::vector<std::string> Parser::split_parametros(std::string cadena,char delimitador){
	//creo el vector de strings para devolver
	std::vector<std::string> split;
	//string del parametro encontrado, es temporal
	std::string param_encontrado;
	//si la cadena no esta vacia
	if(cadena != ""){
		//defino las variables de posicion. Esto se debe a que busco
		//la posicion inicial del delimitador, la del proximo, y copio
		//lo que hay entre estas
		size_t pos_inicial=0;
		size_t pos_final = cadena.find_first_of(delimitador,pos_inicial);
		
		while(pos_final != std::string::npos){
			//copio el contenido
			param_encontrado = cadena.substr(pos_inicial, pos_final - pos_inicial);
			//lo agrego al vector que devuelvo
			split.push_back(param_encontrado);
			//sigo con el proximo
			pos_inicial = pos_final+1;
			pos_final = cadena.find_first_of(delimitador,pos_inicial);
		}
		//el ultimo param no va a encontrar su posicion final, ya que el
		//ultimo no tiene un delimitador
		param_encontrado = cadena.substr(pos_inicial, pos_final - pos_inicial);
		split.push_back(param_encontrado);
	}
	//devuelvo el vector
	return split;
}

bool Parser::es_string(const std::string &cadena){
	std::vector<std::string> params = split_parametros(cadena, '\'');
	//Si cant de params != 3, o el param 0 o el param 2 son distintos a cadena nula => sintaxis incorrecta
	if ((params.size() != 3) || (params[0].size() != 0) || (params[2].size() != 0))
		return false;
	//Si el nombre del archivo es nulo no es string
	if (params[1].size() == 0)
		return false;	
	return true;
}

void Parser::estan_separados_bien(const std::string& cadena) throw (ExcepSintaxisParser){
	bool dentro_struct = false;
	char anterior = 'a'; //cualquiera
	
	for(unsigned int pos = 0; pos < cadena.size() ; pos++){
		if( (!dentro_struct) && (anterior != '[') && (anterior != ',') && (cadena[pos] == '|') )
			throw ExcepSintaxisParser("Error - Sintaxis Incorrecta. Falta ',' para separar.");
		if( (dentro_struct) && (anterior == ',') && (cadena[pos] == '|') )
			throw ExcepSintaxisParser("Error - Sintaxis Incorrecta. Falta un parametro despues de la ','.");
		if( (dentro_struct) && (cadena[pos] == ',') && (anterior == '|') )
			throw ExcepSintaxisParser("Error - Sintaxis incorrecta. Coma mal ubicada.");	
		if( cadena[pos] == '|'	)
			dentro_struct = !dentro_struct;
		anterior = cadena[pos];
			
	}	
	
}

bool Parser::procesar_nombre_operacion(std::string &param, std::string &nombre_operacion){
	bool _retorno = false;
	std::string nombre;
	//separo primero el nombre de la operacion del resto
	size_t pos_esp = param.find_first_of(' ');
	if (pos_esp != std::string::npos) {
		nombre = param.substr(0, param.find_first_of(' '));
		param = param.substr(param.find_first_of(' ') + 1, param.size() - 1);
		//saco los espacios en blanco
		trim(param);
	} else {
		nombre = param;
		param = "";
	}
	_retorno = ( nombre.compare(nombre_operacion) == 0 );
	return _retorno;			
}

bool Parser::convertir_a_int(std::string &dato,int& campo){
	if( ('0' > dato[0] ) || ('9' < dato[0]) )
		return false;
	int val;
	std::stringstream ss;
	std::string aux;
	ss<<(dato)<< '\n';
	ss>>val;
	bool error = !(ss.good());
	ss>>aux;
	if ((!error) && (aux.size() == 0)){
		campo = val;
		return true;
	}
	return false;	
}

std::vector<TipoDato *> Parser::obtener_datos_atomicos (const std::string &expr)
		  												throw (ExcepSintaxisParser){
	std::vector< TipoDato *> resultado; //Vector que contiene los datos convertidos
	
	//Tomo la expr y la spliteo segun las ','
	std::vector< std::string > datos = Parser::split_parametros(expr, ',');
	//Recorro todo el vector
	std::vector< std::string >::iterator it = datos.begin();
	while (it != datos.end() ){
		//Obtengo el valor del it. Me fijo que la cadena no este vacia
		std::string aux = (*it);	
		if (aux.size() != 0) {
			try{
				TipoDato* dato = Parser::procesar_valor(aux);
				resultado.push_back(dato);
			}
			catch(ExcepSintaxisParser &e){
				//libero y la relanzo
				std::vector<TipoDato *>::iterator it = resultado.begin();
				while(it != resultado.end() ){
					delete (*it);
					++it;	
				}
				throw e;
			}
		}
		it++;
	}
	return resultado;
}

TipoDato* Parser::procesar_campo_impar(std::string expresion, unsigned int num_campo, TipoStruct* reg_base) 
																	throw (ExcepSintaxisParser){
	if ((expresion[0] == '|') && (expresion[expresion.size()-1] == '|')){
		//saco las barras
		expresion.erase(0,1);	
		expresion.erase(expresion.size() - 1, 1);
		TipoDato* elem;
		TipoStruct* reg;
		TipoDato* aux = &((*reg_base)[num_campo]);
		if (aux->get_codigo_tipo() == COLECCION)
			reg = (TipoStruct*)((TipoColec*) aux)->get_tipo_modelo();
		else
			if (aux->get_codigo_tipo() == STRUCT)
				reg = (TipoStruct*) aux->clonar();
			else
				throw ExcepSintaxisParser("Error - Los datos no son congruentes con el formato del archivo que se quiere consultar.");
		TipoColec* colec = new TipoColec(*reg, aux->es_obligatorio());
		
		std::vector<TipoDato*> datos;
		try {
			datos = Parser::obtener_datos_atomicos(expresion);
			std::vector<TipoDato*>::iterator it = datos.begin();
			
			while (it != datos.end()) {
				//Cargo el reg
				unsigned int cont = 0;
				while ((it != datos.end()) && (cont < reg->get_tamanio())){
					elem = (*it);
					(*reg)[cont] = *elem;
					it++;
					cont++;
				}
				//Lo meto en la colec
				colec->add_dato(*reg);
			}
			it = datos.begin();
			while(it != datos.end() ){
				delete (*it);
				++it;	
			}
			
		} catch (ExcepSintaxisParser &e) {
			delete(colec);
			delete(reg);
			std::vector<TipoDato *>::iterator it = datos.begin();
			while(it != datos.end() ){
				delete (*it);
				++it;	
			}
			throw e;
		} catch (ExcepErrorTipos &e2) {
			delete(colec);
			delete(reg);
			std::vector<TipoDato *>::iterator it = datos.begin();
			while(it != datos.end() ){
				delete (*it);
				++it;	
			}
			throw ExcepSintaxisParser("Error  - Incongruencia de tipos");
		}
		delete(reg);
		return colec;
	}
	else
		return procesar_valor(expresion);
}

TipoDato* Parser::procesar_valor(std::string aux) throw (ExcepSintaxisParser){
	int aux_int;
	TipoDato * dato = NULL;
	//Veo si es un string
	if (Parser::es_string(aux))
		//Si es string	lo meto en un tipo de dato string y lo pongo en el vector de resultados
		dato = new TipoString(aux.substr(1, (aux.size()) - 2));
	else{ //sino intento convertir a int
		if (convertir_a_int(aux, aux_int)) 
			//Si pude convertir meto el int en un dato int y lo pongo en el vector de resultados	
			dato = new TipoInt(aux_int);
		else //sino arrojo una excepcion por falla de sintaxis
			throw ExcepSintaxisParser ("Error - Sintaxis de datos de entrada incorrecta.");
	}
	return dato;
}


void Parser::procesar_datos_secundarios(const std::string &expr, TipoStruct* registro,
								Funcion &procesar_atom, Funcion &procesar_struct)
								throw (ExcepSintaxisParser){
	//Me fijo si la cadena de datos secs esta encerrada por [], sino arrojo excepcion
	if ((expr[0] == '[') && (expr[expr.size() - 1] == ']')) {
		//reviso la sintaxis en primera instancia
		estan_separados_bien(expr);
		revisar_comas(expr);
		//Hago un split de la cadena segun '|'. Almaceno en un vector de strings los resultados.
		//En las posiciones pares del vector quedaran los datos atomicos separados por comas,
		//y en las impares los datos dentro de un struct determinado, tmb separados por comas.
		std::vector< std::string > conjuntos_datos = split_parametros(expr, '|');
		unsigned int pos = 0;
		//saco los corchetes
		conjuntos_datos[0].erase(0,1);	
		conjuntos_datos[conjuntos_datos.size() - 1].erase(conjuntos_datos[conjuntos_datos.size() - 1].size() - 1, 1);
		
		//si el tamanio del conj de datos es par, es porq termina en un struct
		//si termina en un struct verifico que el ultimo caracter sea un '|'
		if ( (conjuntos_datos.size() % 2) == 0) {
			std::string s_aux = conjuntos_datos[conjuntos_datos.size() - 1];
			if (s_aux[s_aux.size() - 1] != '|')
				throw ExcepSintaxisParser("Error - Sintaxis incorrecta. Falta cerrar una estructura.");
		}
		
		//Recorro todos los conjuntos de datos secundarios y los proceso. Los pares como atomicos
		//y los impares como structs.
		for (unsigned int cont = 0; cont < conjuntos_datos.size(); cont++){
			try {
				if ( (cont%2) == 0)
					pos = procesar_atom(conjuntos_datos[cont], registro, pos);
				else
					pos = procesar_struct(conjuntos_datos[cont], registro, pos);
			} catch (ExcepErrorTipos  e) {
				throw ExcepSintaxisParser("Error - Sintaxis de datos de entrada incorrecta.");	
			}
		}
	} else								
			throw ExcepSintaxisParser("Error - Sintaxis de datos de entrada incorrecta.");						
}										

void Parser::procesar_ingreso(std::string datos_sec, TipoStruct* reg) throw (ExcepSintaxisParser){
		//Creo las funciones que se encargan de procesar los datos atomicos y las estructuras
		FuncProcAtomIng proc_atom;
		FuncProcStructIng proc_struct;
		
		//Parseo los datos secundarios, segun la expresion leida desde el archivo de comandos,
		//el reg base del archivo y las funciones previamente creadas.
		//Si hay error procesando me va a tirar excepcion que se propaga.
		Parser::procesar_datos_secundarios(datos_sec, reg, proc_atom, proc_struct);
		
		//Reestructuro el registro, para llenar los obligatorios que faltan con los optativos
		//que sobran.
		reestructurar(reg);
		
		//Me fijo si cargue el registro completamente, sino arrojo excep. 
		if (!reg->esta_cargado())
			throw ExcepSintaxisParser("Error - Sintaxis de datos de entrada incorrecta.");
	
}

void Parser::revisar_comas(const std::string& expresion) throw (ExcepSintaxisParser){
	char anterior = '1'; //cualquiera
		
	for(unsigned int num_letra = 0; num_letra < expresion.size(); num_letra++ ){
		if(  ((anterior == '[') || (anterior == ']') || (anterior == ';') || (anterior == ',')) && (expresion[num_letra] == ','))
			throw ExcepSintaxisParser("Error - Sintaxis incorrecta. Coma mal ubicada.");
		if(  ((expresion[num_letra] == '[') || (expresion[num_letra] == ']') || (expresion[num_letra] == ';') || (expresion[num_letra] == ',')) && (anterior == ','))
			throw ExcepSintaxisParser("Error - Sintaxis incorrecta. Coma mal ubicada.");	
			
		anterior = expresion[num_letra];
	}
	
}
std::string Parser::procesar_nombre(std::string nombre) throw (ExcepSintaxisParser){
		std::vector<std::string> params = split_parametros(nombre, '\'');
		//Si cant de params != 3, o el param 0 o el param 2 son distintos a cadena nula => sintaxis incorrecta
		if ((params.size() != 3) || (params[0].size() != 0) || (params[2].size() != 0))
			throw ExcepSintaxisParser("Error - Sintaxis del nombre de archivo incorrecta.");
		//Si el nombre del archivo es nulo arrojo excep
		if (params[1].size() == 0)
			throw ExcepSintaxisParser("Error - Nombre de archivo nulo.");
		return params[1];
}
		
void Parser::reestructurar(TipoStruct* reg) {
		int cont = reg->get_tamanio() - 1;
		while (cont >= 0) {
			TipoDato* aux1 = &((*reg)[cont]);
			if ( (aux1->get_codigo_tipo() == COLECCION) && (aux1->es_obligatorio()) && (!aux1->esta_cargado()) ) {
				bool termine = false;
				unsigned int cont2 = cont - 1;
				while ((cont2 >= 0) && (!termine)) {
					TipoDato* aux2 = &((*reg)[cont2]);
					if (aux2->get_codigo_tipo() == COLECCION) {
						if (!aux2->es_obligatorio()) {
							if (aux1->mismo_tipo(*aux2)) {
								if (aux2->esta_cargado()) {
									(*aux1) = (*aux2);
									((TipoColec*) aux1)->set_obligatorio(true);
									((TipoColec*) aux2)->clear();
									termine = true;
								}
							} else {
								if (aux2->esta_cargado())
									termine = true;	
							}
						} else {
							if (aux2->esta_cargado())
								termine = true;	
						}
					} else
						termine = true;
					cont2--;
				}
			}
			cont--;
		}
}

void Parser::trim(std::string &cadena) {
	bool prendido = true;
	unsigned int cont = 0;
	while (cont < cadena.size()) {
		if ( cadena[cont] == '\'' )
			prendido = !prendido;
		if ( (prendido) && (cadena[cont] == ' ') )
			cadena.erase(cont, 1);
		else
			cont++;
	}
}

void Parser::revisar_pcoma_dentro_struct(std::string &expresion) throw (ExcepSintaxisParser){
	bool dentro_struct = false;
	
	for(unsigned int pos = 0; pos < expresion.size() ; pos++){
		if( expresion[pos] == '|'	)
			dentro_struct = !dentro_struct;
		else
			if( dentro_struct && (expresion[pos] == ';') )
				throw ExcepSintaxisParser("Error - Hay un ';' dentro de un struct.");
	}	
}
